﻿using System.Text;
namespace System.Web.Hosting
{
    internal class RecyclableCharBuffer
    {
        private byte[] _byteBuffer;
        private char[] _charBuffer = ((char[])_charBufferAllocator.GetBuffer());
        private int _freePos;
        private bool _recyclable;
        private int _size;
        private const int BUFFER_SIZE = 0x400;
        private const int MAX_FREE_BUFFERS = 0x40;
        private static UbyteBufferAllocator _byteBufferAllocator = new UbyteBufferAllocator(Encoding.UTF8.GetMaxByteCount(0x400), 0x40);
        private static CharBufferAllocator _charBufferAllocator = new CharBufferAllocator(0x400, 0x40);

        internal RecyclableCharBuffer()
        {
            _size = _charBuffer.Length;
            _freePos = 0;
            _recyclable = true;
        }

        internal void Append(char ch)
        {
            if (_freePos >= _size)
                Grow(_freePos + 1);
            _charBuffer[_freePos++] = ch;
        }

        internal void Append(string s)
        {
            int length = s.Length;
            int newSize = _freePos + length;
            if (newSize > _size)
                Grow(newSize);
            s.CopyTo(0, _charBuffer, _freePos, length);
            _freePos = newSize;
        }

        internal void Dispose()
        {
            if (_recyclable)
            {
                if (_charBuffer != null)
                    _charBufferAllocator.ReuseBuffer(_charBuffer);
                if (_byteBuffer != null)
                    _byteBufferAllocator.ReuseBuffer(_byteBuffer);
            }
            _charBuffer = null;
            _byteBuffer = null;
        }

        internal byte[] GetEncodedBytesBuffer()
        {
            return GetEncodedBytesBuffer(Encoding.UTF8);
        }

        internal byte[] GetEncodedBytesBuffer(Encoding encoding)
        {
            if (_byteBuffer == null)
            {
                if (encoding == null)
                    encoding = Encoding.UTF8;
                Append('\0');
                if (_recyclable)
                {
                    _byteBuffer = (byte[])_byteBufferAllocator.GetBuffer();
                    if (_freePos > 0)
                        encoding.GetBytes(_charBuffer, 0, _freePos, _byteBuffer, 0);
                }
                else
                    _byteBuffer = encoding.GetBytes(_charBuffer, 0, _freePos);
            }
            return _byteBuffer;
        }

        private void Grow(int newSize)
        {
            if (newSize > _size)
            {
                if (newSize < (_size * 2))
                    newSize = _size * 2;
                var destinationArray = new char[newSize];
                if (_freePos > 0)
                    Array.Copy(_charBuffer, destinationArray, _freePos);
                _charBuffer = destinationArray;
                _size = newSize;
                _recyclable = false;
            }
        }

        public override string ToString()
        {
            return ((_charBuffer != null) && (_freePos > 0) ? new string(_charBuffer, 0, _freePos) : null);
        }
    }
}
